package com.huas.service.serviceimpl;

import com.huas.constant.MessageConstant;
import com.huas.constant.UserConstant;
import com.huas.dto.UserCodeDTO;
import com.huas.entity.LoginUser;
import com.huas.entity.User;
import com.huas.exception.AccountException;
import com.huas.mapper.LoginMapper;
import com.huas.mapper.UserMapper;
import com.huas.properties.JwtProperties;
import com.huas.service.LoginService;
import com.huas.utils.JwtUtil;
import com.huas.utils.RedisUtils;
import com.huas.utils.SecurityUtils;
import com.huas.vo.UserCodeVO;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private LoginMapper loginMapper;
    @Resource
    private JwtProperties jwtProperties;
    @Resource
    private RedisUtils redisUtils;

    /**
     * 登录
     */
    @Override
    public String login(UserCodeDTO userCodeDTO) {
        //1、authenticationManager.authenticate()进行用户认证
        log.info("SpringSecurity进行用户认证");
        UsernamePasswordAuthenticationToken AuthenticationToken =
                new UsernamePasswordAuthenticationToken(userCodeDTO.getAccount(), userCodeDTO.getPassword());
        //需要Authentication类型的对象
        //该类型是接口，就获取该类型的实现类对象
        Authentication authenticate;
        try {
            authenticate = authenticationManager.authenticate(AuthenticationToken);
        } catch (Exception e) {
            throw new AccountException(MessageConstant.LOGIN_FAILED);
        }
        if (Objects.isNull(authenticate)) {
            throw new AccountException(MessageConstant.LOGIN_FAILED);
        }
        //2、验证码校验
        if (!verifyCode(userCodeDTO.getKey(), userCodeDTO.getCode())) {
            throw new AccountException(MessageConstant.CAPTCHA_ERROR);
        }
        //3、用户角色校验
        if (!verifyRoleId(userCodeDTO.getAccount(), userCodeDTO.getRoleId())) {
            throw new AccountException(MessageConstant.ACCOUNT_ROLE_ERROR);
        }
        //4、认证通过
        log.info("登录成功");
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        //5、获取用户id存入jwt
        Integer id = loginUser.getUser().getId();
        HashMap<String, Object> claims = new HashMap<>();
        claims.put(UserConstant.ACCOUNT_ID, id);

        //6、将完整的用户信息存入redis(提高查询效率)
        redisUtils.set(UserConstant.LOGIN_MESSAGE + id, loginUser);
        log.info("用户信息已存入redis,key为{}", UserConstant.LOGIN_MESSAGE + id);

        //7、删除已经验证成功的验证码
        redisUtils.del(UserConstant.LOGIN_CODE + userCodeDTO.getKey());

        //8、返回jwt
        return JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);
    }

    /**
     * 注册
     */
    @Override
    @Transactional
    public void register(User user) {
        //判断是否有重复的用户账号
        String account = user.getAccount();
        User u = loginMapper.findByAccount(account);
        if (!ObjectUtils.isEmpty(u)) {
            throw new AccountException(MessageConstant.ALREADY_EXIST);
        }
        //SpringSecurity框架密码校验格式
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        //注意：默认注册普通用户，昵称默认为账号（可自主修改）
        user.setName(account);
        user.setRoleId(UserConstant.COMMON_ROLE_ID);
        loginMapper.autoIncrement();//确保自增主键连续（前提是表中已存在的有效id小于当前自增值）
        loginMapper.register(user);
    }

    /**
     * 退出登录
     */
    @Override
    public void logout() {
        //获取当前用户的用户id
        Integer id = SecurityUtils.getUserId();
        //从redis删除缓存
        redisUtils.del(UserConstant.LOGIN_MESSAGE + id);
        log.info("用户缓存信息已从redis删除");
    }

    /**
     * 生成验证码
     *
     * @return
     */
    @Override
    public UserCodeVO getCaptcha() {
        //算术类型验证码
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(135, 35, 2);
        //获取运算结果
        String result = "";
        try {
            //result = new Double(Double.parseDouble(captcha.text())).intValue() + "";
            result = Double.valueOf(Double.parseDouble(captcha.text())).intValue() + "";
        } catch (Exception e) {
            result = captcha.text();
        }
        //生成唯一的验证码Key
        UUID key = UUID.randomUUID();
        //存储到redis,过期时间120s
        redisUtils.set(UserConstant.LOGIN_CODE + key, result, UserConstant.LOGIN_CODE_TIME);
        return new UserCodeVO(captcha.toBase64(), key.toString());
    }

    /**
     * 验证码校验逻辑
     */
    private boolean verifyCode(String key, String code) {
        String result = (String) redisUtils.get(UserConstant.LOGIN_CODE + key);
        //判断是否能从redis获取到验证码
        if (result == null) {
            return false;
        }
        return result.equals(code);
    }

    /**
     * 用户角色校验
     */
    private boolean verifyRoleId(String account, Integer roleId) {
        User user = loginMapper.findByAccount(account);
        //如果用户是普通用户，校验角色
        //user.getRoleId()：数据库中用户的角色
        //形参roleId：用户登录时选择的角色
        if (!user.getRoleId().equals(UserConstant.ADMIN_ROLE_ID)) {
            if (!roleId.equals(UserConstant.COMMON_ROLE_ID)) {
                return false;
            }
        }
        return true;
    }
}
